#ifndef __open_vip_axi4__
#define __open_vip_axi4__

#include <string>
#include <vector>
#include <queue>
#include <sys/types.h>
#include "xspcomm/xport.h"
#include "xspcomm/xclock.h"

namespace ovip {

class AXI4MasterPort {
public:
    const uint8_t AxSIZE = 0x3;
    enum BurstType { FIXED = 0, INCR = 1, WARP = 2};

    typedef std::vector<uint64_t> DataVec;

public:
    AXI4MasterPort(std::string name, xspcomm::XPort &dut_port, xspcomm::XClock &clk,
                   std::string prefix);

    ~AXI4MasterPort();

    void reset_status();

    /* READ */
    bool send_read_address(uint32_t addr);
    bool receive_read_data(uint64_t& data);

    /* READ BURST */
    bool send_read_burst(uint32_t addr, uint8_t len, BurstType burst_type = INCR);
    bool receive_read_burst(DataVec& data);

    /* WRITE */
    bool write(uint32_t addr, uint64_t data);
    bool write_burst(uint32_t addr, const DataVec& data, BurstType burst_type = INCR);
    bool receive_write_response(uint8_t& resp);

    /* WRITE CHANNEL FUNCTION */
    bool send_write_address(uint32_t addr, uint8_t len, BurstType burst_type = INCR);
    bool send_write_data(const std::vector<uint64_t>& data);
public:
    std::string mName;
    std::string mPrefix;




// ----------------------------------------------
//  PRIVATE SECTION BELOW
// ----------------------------------------------

private:
    void axi4_step();
    void process_r();
    void process_ar();
    void process_b();
    void process_aw();
    void process_w();

private:
    xspcomm::XPort *all_ports;
    xspcomm::XClock *clk;

    // Channel AR
    xspcomm::XPort *ar_port;
    bool ar_busy;

    // Channel R
    xspcomm::XPort *r_port;
    DataVec data_read;
    bool data_read_valid;

    // Channel AW
    xspcomm::XPort *aw_port;
    bool aw_busy;

    // Channel W
    xspcomm::XPort *w_port;
    std::queue<uint64_t> data_to_write;
    bool w_busy;

    // Channel B
    xspcomm::XPort *b_port;
    uint8_t b_resp;
    bool b_resp_valid;
};

}

#endif
